<template>
  <teleport to="body">
    <div
      v-if="visible"
      class="context-menu-backdrop"
      @click="handleBackdropClick"
    >
      <div
        ref="menuRef"
        class="context-menu"
        :style="menuStyle"
        @click.stop
        @contextmenu.stop.prevent
        @keydown.up.prevent="navigateMenu(-1)"
        @keydown.down.prevent="navigateMenu(1)"
        @keydown.enter.prevent="activateSelectedItem"
        tabindex="-1"
      >
        <div class="context-menu-content">
          <slot :activeIndex="activeItemIndex" @select-item="setActiveItem"></slot>
        </div>
      </div>
    </div>
  </teleport>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, defineProps, defineEmits, watch, nextTick, provide } from 'vue'

const props = defineProps({
  visible: Boolean,
  x: Number,
  y: Number,
  data: Object,
})

const emit = defineEmits(['update:visible', 'close'])

const menuRef = ref(null)
const activeItemIndex = ref(-1)
const menuItems = ref([])

// 处理背景点击
const handleBackdropClick = (event) => {
  // 点击背景关闭菜单，但不传播事件
  event.preventDefault();
  event.stopPropagation();
  emit('update:visible', false)
  emit('close')
}

// 处理ESC键关闭
const handleKeyDown = (event) => {
  if (event.key === 'Escape' && props.visible) {
    emit('update:visible', false)
    emit('close')
  }
}

// 菜单导航
const navigateMenu = (direction) => {
  // 获取可见且非禁用的菜单项数量
  const itemCount = menuItems.value.length
  if (itemCount === 0) return

  // 计算新的索引
  let newIndex = activeItemIndex.value + direction
  
  // 循环导航
  if (newIndex < 0) newIndex = itemCount - 1
  if (newIndex >= itemCount) newIndex = 0
  
  // 更新当前激活的菜单项
  activeItemIndex.value = newIndex
  
  // 确保激活的菜单项可见
  nextTick(() => {
    const activeItem = menuItems.value[activeItemIndex.value]
    if (activeItem && activeItem.$el) {
      activeItem.$el.scrollIntoView({ block: 'nearest' })
    }
  })
}

// 激活当前选中的菜单项
const activateSelectedItem = () => {
  if (activeItemIndex.value >= 0 && activeItemIndex.value < menuItems.value.length) {
    const activeItem = menuItems.value[activeItemIndex.value]
    if (activeItem && !activeItem.disabled) {
      activeItem.click()
    }
  }
}

// 设置当前激活的菜单项
const setActiveItem = (index) => {
  activeItemIndex.value = index
}

// 注册菜单项
const registerMenuItem = (item) => {
  menuItems.value.push(item)
  return menuItems.value.length - 1
}

// 注销菜单项
const unregisterMenuItem = (item) => {
  const index = menuItems.value.indexOf(item)
  if (index !== -1) {
    menuItems.value.splice(index, 1)
    // 更新当前激活项索引
    if (activeItemIndex.value >= menuItems.value.length) {
      activeItemIndex.value = menuItems.value.length - 1
    }
  }
}

// 提供菜单上下文给子组件
provide('contextMenu', {
  registerMenuItem,
  unregisterMenuItem,
  activeItemIndex,
})

// 监听visible变化
watch(() => props.visible, (newVal) => {
  if (newVal) {
    // 如果菜单显示，添加ESC键监听并调整位置
    document.addEventListener('keydown', handleKeyDown)
    adjustPosition()
    // 重置菜单项和激活索引
    menuItems.value = []
    activeItemIndex.value = -1
    // 聚焦菜单，使键盘导航生效
    nextTick(() => {
      if (menuRef.value) {
        menuRef.value.focus()
      }
    })
  } else {
    // 如果菜单隐藏，移除ESC键监听
    document.removeEventListener('keydown', handleKeyDown)
  }
})

// 位置调整
const adjustPosition = () => {
  nextTick(() => {
    if (!menuRef.value) return
    
    const rect = menuRef.value.getBoundingClientRect()
    const viewportWidth = window.innerWidth
    const viewportHeight = window.innerHeight
    
    // 如果菜单会超出右边界，显示在左侧
    if (props.x + rect.width > viewportWidth) {
      menuRef.value.style.left = `${props.x - rect.width}px`
    } else {
      // 确保设置回默认值，以防之前的调整影响了当前的菜单
      menuRef.value.style.left = `${props.x}px`
    }
    
    // 如果菜单会超出下边界，显示在上方
    if (props.y + rect.height > viewportHeight) {
      menuRef.value.style.top = `${props.y - rect.height}px`
    } else {
      // 确保设置回默认值，以防之前的调整影响了当前的菜单
      menuRef.value.style.top = `${props.y}px`
    }
  })
}

// 监听位置变化，重新调整菜单位置
watch([() => props.x, () => props.y], () => {
  if (props.visible) {
    adjustPosition()
  }
})

// 组件销毁时清理
onUnmounted(() => {
  document.removeEventListener('keydown', handleKeyDown)
})

// 计算菜单样式
const menuStyle = computed(() => {
  // 默认位置
  return {
    position: 'absolute',
    left: `${props.x || 0}px`,
    top: `${props.y || 0}px`,
  }
})
</script>

<style scoped>
.context-menu-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 9998;
  background: transparent;
}

.context-menu {
  position: absolute;
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  z-index: 9999;
  min-width: 160px;
  user-select: none;
  animation: contextMenuFadeIn 0.15s ease-out;
  transform-origin: top left;
  outline: none; /* 移除焦点轮廓 */
  max-height: 80vh; /* 限制最大高度 */
  overflow-y: auto; /* 添加滚动条 */
}

@keyframes contextMenuFadeIn {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(-4px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

.context-menu-content {
  padding: 4px 0;
}
</style> 